package com.luxsoft.siipap.em.replica.importadores.cxc;

import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.BeanUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SqlParameterValue;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.util.Assert;

import com.luxsoft.siipap.cxc.dao.NotaDeCreditoDao;
import com.luxsoft.siipap.cxc.model.Aplicacion;
import com.luxsoft.siipap.cxc.model.AplicacionDeNota;
import com.luxsoft.siipap.cxc.model.Cargo;
import com.luxsoft.siipap.cxc.model.NotaDeCredito;
import com.luxsoft.siipap.cxc.model.NotaDeCreditoBonificacion;
import com.luxsoft.siipap.cxc.model.NotaDeCreditoDescuento;
import com.luxsoft.siipap.cxc.model.NotaDeCreditoDevolucion;
import com.luxsoft.siipap.cxc.model.OrigenDeOperacion;
import com.luxsoft.siipap.cxc.model.NotaDeCreditoBonificacion.Concepto;
import com.luxsoft.siipap.cxc.model.NotaDeCreditoDescuento.TipoDeDescuento;
import com.luxsoft.siipap.em.replica.importadores.ImportadorSupport;
import com.luxsoft.siipap.em.replica.importadores.inventarios.ImportadorDeDevoluciones;
import com.luxsoft.siipap.em.replica.importadores.ventas.ImportadorDeVentas;
import com.luxsoft.siipap.model.CantidadMonetaria;
import com.luxsoft.siipap.model.Periodo;
import com.luxsoft.siipap.model.core.Cliente;
import com.luxsoft.siipap.service.ServiceLocator2;
import com.luxsoft.siipap.util.DBUtils;
import com.luxsoft.siipap.util.MonedasUtils;
import com.luxsoft.siipap.util.ValidationUtils;
import com.luxsoft.siipap.ventas.model.Devolucion;
import com.luxsoft.siipap.ventas.model.DevolucionDeVenta;
import com.luxsoft.siipap.ventas.model.Venta;

/**
 * Importa las notas de credito
 * 
 * @author Ruben Cancino Ramos
 *
 */
public class ImportadorDeNotas {
	
	private int total;
	
	private int current;
	
	private Logger logger=Logger.getLogger(getClass());
	
	private NotaDeCreditoDao notaDeCreditoDao;
	
	protected void setProgress(int progress){
		logger.info("Progreso: "+progress);
	}
	
	protected void updateCurrent(){
		current++;		
		if(total!=0){
			int progress=current*100/this.total;			
			setProgress(progress);
		}
	}
	
	/**
	 * Importa o actualiza una nota en el nuevo sistema a partir
	 * del id en siipapwin
	 * 
	 * @param id
	 * @return
	 */
	public NotaDeCredito importar(Long id){
		String sql="SELECT * FROM V_NOTAS_MYSQL WHERE NOTA_ID=?";
		List<NotaDeCredito> res=getOrigenTemplate().query(sql, new Object[]{id}, new NotaMapper());		
		if(res.isEmpty()){
			logger.info("No econtro la nota en siipapwin Nota_ID="+id);
			return null;
		}
		NotaDeCredito nota= res.get(0);
		try {
			try {
				insertarAplicaciones(nota);
			} catch (Exception e) {
				System.out.println("Error importando notasdet: "+ExceptionUtils.getRootCauseMessage(e));
			}
			return persistir(nota);
		} catch (Exception e) {
			logger.error(e);
			e.printStackTrace();
			return null;
		}
	}
	
	/**
	 * Importa todas las notas de un periodo para el tipo indicado
	 * 
	 * @param periodo
	 * 
	 */
	public void importar(final Periodo periodo){
		
		String sql="SELECT A.* FROM V_NOTAS_MYSQL  A WHERE A.FECHA BETWEEN ? AND ?";
		Object[] params=new Object[]{
			new SqlParameterValue(Types.DATE,periodo.getFechaInicial())	
			,new SqlParameterValue(Types.DATE,periodo.getFechaFinal())
		};
		int tot=getOrigenTemplate().queryForInt("SELECT count(A.NOTA_ID) FROM V_NOTAS_MYSQL  A WHERE A.FECHA BETWEEN ? AND ?",params);
		logger.info("Total de registros a importar: "+tot );
		setTotal(tot);
		logger.info(MessageFormat.format("Mapeando para el periodo {0}",periodo));
		List<NotaDeCredito> notas=getOrigenTemplate().query(sql, params,new NotaMapper());
		for (NotaDeCredito notaDeCredito : notas) {
			if(notaDeCredito==null) continue;
			try {
				System.out.println("Importando nota: "+notaDeCredito.getSiipapId());
				insertarAplicaciones(notaDeCredito);				
				notaDeCredito=persistir(notaDeCredito);
				updateCurrent();
			} catch (Exception e) {
				e.printStackTrace();
				logger.error(e);
			}
			 	
		}
	}
	
	protected NotaDeCredito persistir(final NotaDeCredito nota){
		if(!ValidationUtils.isValid(nota)){
			ValidationUtils.debugValidation(nota);
		}
		NotaDeCredito target=getDao().buscarPorSiipapId(nota.getSiipapId());
		if(target==null){			
			target=nota;
		}
		else{
			//copiar propiedades necesarias
			logger.info("Persistiendo nota existente: "+target.getSiipapId());
			target.getAplicaciones().clear();
			BeanUtils.copyProperties(nota, target,new String[]{"id","version","aplicaciones"});			
			for(Aplicacion a:nota.getAplicaciones()){
				target.agregarAplicacion(a);
				a.actualizarDetalle();
			}
			
		}		
		NotaDeCredito res= (NotaDeCredito)getHibernate().merge(target);
		if(res instanceof NotaDeCreditoDevolucion)
			actualizarDevoluciones((NotaDeCreditoDevolucion)res);
		return res;
	}
	
	private void actualizarDevoluciones(final NotaDeCreditoDevolucion nota){
		
		Devolucion dev=nota.getDevolucion();
		String sql="select DEVODET_ID from v_devs where nota_id=?";		
		List<Long> rows=getOrigenTemplate().queryForList(sql,new Object[]{nota.getSiipapId()},Long.class);
		
		for(DevolucionDeVenta det:dev.getPartidas()){
			if(rows.contains(det.getAlmacenId()))
				det.setNota(nota);
		}
		getHibernate().merge(dev);
		
	}
	
	private void insertarAplicaciones(final NotaDeCredito nota){
		//String sql="select b.FECHA ,a.* from SW_NOTASDET a left join SW_NOTAS b on(a.NOTA_ID=b.NOTA_ID) where a.nota_id=? order by a.NOTADET_ID ASC";
		String sql="select X.APLICABLE AS APLIC,A.ORIGEN,A.TIPO,A.VENTA_ID,A.DETALLEIDX,A.DESCUENTO,A.COMENTARIO,A.FECHA,A.IMPORTE,A.NOTADET_ID,A.NOTA_ID " +
				"FROM SW_NOTASDET A JOIN SW_NOTAS X ON(X.NOTA_ID=A.NOTA_ID) " +
				"WHERE A.YEAR>=2008  AND X.APLICABLE=0 AND A.SERIE <>\'M\' AND A.NOTA_ID=?	" +
				"UNION	" +
				"SELECT 1 AS APLIC,A.ORIGEN,SUBSTR(REFERENCIA,1,1) AS TIPO,A.VENTA_ID,0 AS DTALLEIDX,0 AS DESCUENTO,' ' AS COMENTARIO,A.FECHA,A.IMPORTE,PAGO_ID AS NOTADET_ID,A.NOTAPAGO_ID AS NOTA_ID " +
				"FROM SW_PAGOS A WHERE A.YEAR>=2008 AND A.FORMADEPAGO=\'T\' AND A.NOTAPAGO_ID=? AND A.VENTA_ID IS NOT NULL";

		
		Object[] args=new Object[]{nota.getSiipapId(),nota.getSiipapId()};
		List<AplicacionDeNota> aplicaciones=getOrigenTemplate().query(sql, args, new NotasDetMapper());
		for (AplicacionDeNota a : aplicaciones) {
			if(a==null)continue;
			a.actualizarDetalle();
			nota.agregarAplicacion(a);
		}
	}
	
	private NotaDeCreditoDao getDao(){
		if(notaDeCreditoDao==null)
			notaDeCreditoDao=(NotaDeCreditoDao)ServiceLocator2.instance().getContext().getBean("notaDeCreditoDao");
		return notaDeCreditoDao;
	}
	
	public JdbcTemplate getOrigenTemplate(){
		return ServiceLocator2.getAnalisisJdbcTemplate();
	}

	public int getTotal() {
		return total;
	}

	public void setTotal(int total) {
		this.total = total;
	}
	
	static List<String> BONIFICACIONES=Arrays.asList(new String[]{"L","C","Y","F","O"});
	static List<String> DEVOLUCIONES=Arrays.asList(new String[]{"H","I","J"});
	
	private class NotaMapper implements RowMapper{

		public Object mapRow(ResultSet rs, int rowNum) throws SQLException {			
			
			try {
				int folio=rs.getInt("NUMERO");
				System.out.println("Mapeando nota:"+folio);
				NotaDeCredito nota=null;
				
				String tipo=rs.getString("TIPO");
				//System.out.println("Mapeando tipo:"+tipo);
				
				
				
				if("U".equalsIgnoreCase(tipo)){
					nota=getNota(TipoDeDescuento.ASIGNADO);
				}else if( "V".equalsIgnoreCase(tipo)){
					nota=getNota(TipoDeDescuento.ADICIONAL);
				}else if(BONIFICACIONES.contains(tipo)){
					NotaDeCreditoBonificacion notaBonificacion=new NotaDeCreditoBonificacion();
					if(!tipo.equals("L"))
						notaBonificacion.setConcepto(Concepto.BONIFICACION);
					else{
						String data=rs.getString("BONIFICACION");
						if(data!=null)
							notaBonificacion.setConcepto(Concepto.valueOf(data));
						
					}
					nota=notaBonificacion;
				}else if(DEVOLUCIONES.contains(tipo)){
					NotaDeCreditoDevolucion notaDevo=new NotaDeCreditoDevolucion();
					Devolucion d=buscarDevolucion(rs.getLong("DEVO_ID"));
					if(d==null) return null;
					notaDevo.setDevolucion(d);
					nota=notaDevo;
				}else{
					// No es un tipo adecuado
					return null;
				}
				
				String origen=rs.getString("ORIGEN");
				nota.setSiipapId(rs.getLong("NOTA_ID"));	
				nota.setOrigen(OrigenDeOperacion.valueOf(origen));				
				Cliente cliente=ImportadorSupport.getInstance().buscarCliente(rs.getString("CLAVE"));
				if(cliente==null) cliente=ImportadorSupport.getInstance().agregarCliente(rs.getString("CLAVE"), rs.getString("NOMBRE"));
				if(cliente==null){				
					logger.info("No se pudo importar por cliente nulo siipap_id="+nota.getSiipapId());
					return null;
				}
				nota.setCliente(cliente);
				nota.setComentario(rs.getString("COMENTARIO"));
				nota.setComentario2(rs.getString("COMENTARIO2"));
				nota.setDescuento(rs.getDouble("DESCUENTO")/100);			
				nota.setFecha(rs.getDate("FECHA"));
				
				nota.setFolio(folio);
				
				
				BigDecimal imp=rs.getBigDecimal("IMPORTE").abs();
				CantidadMonetaria importe=CantidadMonetaria.pesos(imp.doubleValue());
				CantidadMonetaria impuesto=MonedasUtils.calcularImpuesto(importe);
				CantidadMonetaria total=MonedasUtils.calcularTotal(importe);
				nota.setImporte(importe.amount());
				nota.setImpuesto(impuesto.amount());
				nota.setTotal(total.amount());
						
				
				//nota.actualizarImpuesto();
				//nota.actualizarTotal();
				nota.setImpreso(rs.getDate("IMPRESO"));
				Boolean ap=rs.getBoolean("APLICABLE");
				nota.setAplicable(ap);		
				nota.setSucursal(ImportadorSupport.getInstance().buscarSucursal(1));
							
				return nota;
			} catch (Exception e) {				
				e.printStackTrace();
				return null;
			}
		}
		
	}
	
	public static NotaDeCredito getNota(TipoDeDescuento tipo){
		NotaDeCreditoDescuento nc=new NotaDeCreditoDescuento();
		nc.setTipoDeDescuento(tipo);
		return nc;
	}
	
	private class NotasDetMapper implements RowMapper{
		public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
			AplicacionDeNota aplicacion=new AplicacionDeNota();
			Long ventaid=rs.getLong("VENTA_ID");
			//if(ventaid==0)
				//return null;
			Cargo cargo=buscarCargo(ventaid);
			aplicacion.setCargo(cargo);
			
			int renglon=rs.getInt("DETALLEIDX");
			aplicacion.reglonSiipap=renglon;
			
			aplicacion.setComentario(rs.getString("COMENTARIO"));
			BigDecimal desc=BigDecimal.valueOf(rs.getDouble("DESCUENTO")/100);
			aplicacion.setDescuentoPorNota(desc);
			aplicacion.setFecha(rs.getDate("FECHA"));
			aplicacion.setImporte(rs.getBigDecimal("IMPORTE").abs());
			aplicacion.setSiipapId(rs.getLong("NOTADET_ID"));
			return aplicacion;
		}
		
	}
	
	/**
	 * Busca una devolucion en el sistema usando el id de oracle, si no 
	 * la encuentra la importa desde oracle
	 * 
	 * @param devoId
	 * @return
	 */
	public Devolucion buscarDevolucion(final Long devoId){		
		String hql="from Devolucion d left join fetch d.partidas where d.siipapWinId=?";
		List<Devolucion> res=getHibernate().find(hql, devoId);
		if(res.isEmpty()){
			Devolucion dev=getImportadorDeDevoluciones().importar(devoId);
			return dev;
		}else
			return res.get(0);
	}
	
	public HibernateTemplate getHibernate(){
		return ServiceLocator2.getHibernateTemplate();
	}
	
	private Cargo buscarCargo(Long id){
		List<Cargo> data=getHibernate().find("from Cargo c  where c.siipapWinId=?", id);
		if(data.isEmpty()){
			//La venta posiblemente no esta importada
			Venta venta=getImportadorDeVentas().importar(id);
			Assert.isTrue(venta!=null,"No se encontro ni se pudo importar la venta:"+id);
			return venta;
		}else{			
			return data.get(0);
		}
		
	}
	
	
	
	
	public ImportadorDeVentas getImportadorDeVentas(){
		return ImportadorDeVentas.instance();
	}
	
	private ImportadorDeDevoluciones importadorDeDevoluciones;
	
	private ImportadorDeDevoluciones getImportadorDeDevoluciones(){
		if(importadorDeDevoluciones==null){
			importadorDeDevoluciones=new ImportadorDeDevoluciones();
		}
		return importadorDeDevoluciones;
	}
	
	private static ImportadorDeNotas INSTANCE;
	
	public static ImportadorDeNotas getInstance(){
		if(INSTANCE==null)
			INSTANCE=new ImportadorDeNotas();
		return INSTANCE;
	}

	public static void main(String[] args) {
		DBUtils.whereWeAre();
		ImportadorDeNotas imp=new ImportadorDeNotas();
		//imp.importar(new Periodo("17/02/2009","17/02/2009"));
		//imp.importar(new Periodo("26/01/2009","26/01/2009"));
		//imp.importar(129422L);		
		/*List<Long> ids=ServiceLocator2.getAnalisisJdbcTemplate().queryForList(
				"select nota_id from nota_id_temp", Long.class);
		int row=0;
		for(Long l:ids){
			
			try {
				imp.importar(l);
				row++;
				System.out.println("Importado: "+l);
			} catch (Exception e) {
				e.printStackTrace();
			}
			
			
		}
		System.out.println("Importados: "+row);*/
		/*List<Long> ids=ServiceLocator2.getAnalisisJdbcTemplate().queryForList("select nota_id from SW_NOTASDET where VENTA_ID in(select venta_id from VENTAS_TO_MYSQL_TEMP)", Long.class);
		System.out.println("Importar: "+ids.size());
		for(Long l:ids){
			imp.importar(l);
			
		}*/
		Long ids[]={125691L,
				127038L,
				127581L,
				128786L,
				128928L,
				129003L,
				129027L,
				129879L,
				130056L,
				130057L,
				130086L,
				130129L,
				130146L,
				130479L,
				130523L,
				130524L,
				130726L,
				130727L,
				130728L,
				130729L,
				130730L,
				130731L,
				130732L,
				130733L,
				130734L,
				130735L,
				130736L,
				130737L,
				130738L,
				130739L,
				130740L,
				130742L,
				130743L,
				130744L,
				130751L,
				130752L,
				130753L,
				130754L,
				130755L,
				130981L};
		int row=0;
		for(Long l:ids){
			
			try {
				imp.importar(l);
				row++;
				System.out.println("Importado: "+l);
			} catch (Exception e) {
				e.printStackTrace();
			}
			
			
		}
		
	}
}
 